import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { AppState } from "@store/index";
import { saveCredential } from "@slice/credential";

// request: 用于发送请求的方法
const request = fetchBaseQuery({
  baseUrl: process.env.REACT_APP_BASE_URL, // 设置请求的基础URL
  // 全局请求头的配置
  prepareHeaders: (headers, api) => {
    // headers 请求头配置对象
    // api 提供了 dispatch、getState 等工具方法
    // 1. 先判断请求头中是否已经存在了 token, 如果存在则不需要再次设置
    if (headers.has("authorization")) return headers;
    // 2. 获取本地 token
    const token = (api.getState() as AppState).credential.token;
    // 3. 判断本地 token 是否存在
    if (token) {
      // 4. 如果本地 token 存在, 将其添加到请求头中
      headers.set("authorization", `Bearer ${token}`);
    }
    return headers; // 返回配置后的请求头
  },
});

export const apiService = createApi({
  reducerPath: "apiReducer", // 为这个API服务设置一个独特的字符串标识，用于在Redux store中标识这个服务
  // 和请求相关的全局配置找 baseQuery
  baseQuery: async (args, api, extraOptions) => {
    // args: 具体请求对应的配置, 比如请求地址、请求方式、请求参数
    // 特别注意: 不管请求是成功的还是失败的, Promise 的状态都会变成fulfilled
    // 请求成功 {data: {}, meta: {}}
    // 请求失败 {error: {}, meta:{}}
    const response = await request(args, api, extraOptions); // 发送请求
    // 判断请求是否是失败的
    if (
      typeof response.error !== "undefined" &&
      response.error.status === 401 // 如果请求失败且状态码为401（未授权）
    ) {
      // 导入 router 对象
      const router = require("@router/index").router;
      const { pathname, search, hash } = router.state.location; // 获取当前路由位置
      // 1. 获取本地存储的 refresh token
      const refresh_token = (api.getState() as AppState).credential.refresh_token;
      // 判断本地是否有 refresh_token
      if (refresh_token) {
        // 3. 如果本地有 refresh token, 向服务端发送请求, 获取新的 token
        const response = await request(
          {
            url: "/authorizations", // 刷新token的API地址
            method: "PUT", // 使用PUT方法请求
            headers: { authorization: `Bearer ${refresh_token}` }, // 在请求头中携带refresh_token
          },
          api,
          extraOptions
        );
        // 判断 token 是否刷新成功
        if (typeof response.error === "undefined") {
          // 4. 如果新的 token 获取成功, 将最新的 token 保存回本地
          api.dispatch(
            saveCredential({
              token: (response.data as GeekResponse<{ token: string }>).data.token,
              refresh_token,
            })
          );
          // 5. 使用新的 token 重新发送之前失败的请求并返回响应
          return request(args, api, extraOptions);
        } else {
          // token 换取失败, 跳转到登录页面
          // 2. 如果本地没有 refresh token, 跳转到登录页面
          await router.navigate(
            { pathname: "/login" },
            { state: { from: pathname + search + hash, replace: true } } // 携带重定向信息
          );
        }
      } else {
        // 2. 如果本地没有 refresh token, 直接跳转到登录页面
        await router.navigate(
          { pathname: "/login" },
          { state: { from: pathname + search + hash }, replace: true }
        );
      }

      // 6. 如果 token 换取失败, 跳转到登录页面
    }
    return response; // 返回响应结果
  },
  endpoints: () => ({}), // 定义API的端点，这里暂时为空
});